package com.yeyks.search.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.yeyks.common.base.Arguments;
import com.yeyks.common.constants.ConfigKey;
import com.yeyks.common.constants.hotel.HotelSourceType;
import com.yeyks.common.exception.ServiceException;
import com.yeyks.common.param.cnhotel.CnHotelListParam;
import com.yeyks.common.param.cnhotel.HotelCountParam;
import com.yeyks.common.result.cnhotel.CnHotelListResult;
import com.yeyks.common.result.cnhotel.CnHotelListResultCount;
import com.yeyks.common.result.cnhotel.ExportCnHotelResult;
import com.yeyks.common.utils.ObjectConvertUtil;
import com.yeyks.common.utils.Splitters;
import com.yeyks.config.dal.dao.ConfigMapper;
import com.yeyks.config.dal.domain.Config;
import com.yeyks.hotel.dal.domain.RoomInfo;
import com.yeyks.other.dal.domain.ImportCityDataConfig;
import com.yeyks.other.service.ImportCityDataConfigService;
import com.yeyks.search.constants.EsIndexConstant;
import com.yeyks.search.pojo.model.EsRoomCalendarInfo;
import com.yeyks.search.pojo.model.base.BasalSearchParam;
import com.yeyks.search.pojo.model.base.EsSort;
import com.yeyks.search.pojo.param.EsC2bHotelSearchParam;
import com.yeyks.search.pojo.param.EsHotelSearchParam;
import com.yeyks.search.pojo.param.export.EsHotelCitySearchParam;
import com.yeyks.search.pojo.param.export.EsHotelRoomCitySearchParam;
import com.yeyks.search.pojo.result.hotel.EsHotelInfoDetailVo;
import com.yeyks.search.pojo.result.hotel.EsHotelInfoVo;
import com.yeyks.search.pojo.result.hotel.EsRoomInfoVo;
import com.yeyks.search.service.ElasticSearchService;
import com.yeyks.search.service.MySearchService;
import javassist.compiler.ast.Keyword;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.geo.GeoDistance;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.*;
import org.elasticsearch.join.query.HasChildQueryBuilder;
import org.elasticsearch.join.query.JoinQueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author shuai
 * @date 2019/10/08 15:40
 */
@Service
@Slf4j
public class MySearchServiceImpl implements MySearchService {

    @Autowired
    private ElasticSearchService elasticSearchService;
    @Autowired
    private ConfigMapper configMapper;
    //es中查询到的总个数
    private Long total = 100L;
    @Autowired
    private ImportCityDataConfigService importCityDataConfigService;

    /**
     * 酒店条件搜索
     *
     * @param param
     * @return
     */
    @Override
    public List<EsHotelInfoVo> searchHotel(EsHotelSearchParam param) {
        //排序字段
        List<EsSort> esSorts = Lists.newArrayList();
        /**
         * BoolQueryBuilder复合查询语法如下：
         * term query精准查询
         * wildcard query 通配符查询，支持* 任意字符串；？任意一个字符
         * range query范围查询（如：QueryBuilders.rangeQuery("hotelNo").gt("10143262306").lt("101432623062055348221") .includeLower(true).includeUpper(false);）
         */
        //内置查询条件的显示查询
        InnerHitBuilder innerHitBuilder = new InnerHitBuilder().setSize(100);
        //三级房态
        HasChildQueryBuilder hasChildQueryBuilder3 = JoinQueryBuilders.hasChildQuery(EsIndexConstant.JOIN_TYPE_CALENDAR, new MatchAllQueryBuilder(), ScoreMode.None);
        hasChildQueryBuilder3.innerHit(innerHitBuilder);
        //二级房间
        HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, hasChildQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder2.innerHit(innerHitBuilder);
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(hasChildQueryBuilder2);
        //排序字段添加
        if (StrUtil.isNotBlank(param.getSortGmtCreate())) {
            if ("Desc".equalsIgnoreCase(param.getSortGmtCreate())) {
                esSorts.add(new EsSort("gmtCreate", SortOrder.DESC));
            } else {
                esSorts.add(new EsSort("gmtCreate", SortOrder.ASC));
            }
        }
        //使用BoolQueryBuilder进行复合查询(must代表and，should代表or)
        //数据库酒店id精准查询
        if (Objects.nonNull(param.getSourceId())) {
            queryBuilder.must(QueryBuilders.termQuery("sourceId", param.getSourceId()));
        }
        //文档唯一键id精准查询
        if (Objects.nonNull(param.getHotelId())) {
            queryBuilder.must(QueryBuilders.termQuery("hotelId", param.getHotelId()));
        }
        //名称模糊查询
        if (StrUtil.isNotBlank(param.getName())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("name", "*" + param.getName() + "*"));
        }
        //地址模糊查询
        if (StrUtil.isNotBlank(param.getAddress())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("address", "*" + param.getAddress() + "*"));
        }
        //星级精准查询
        if (Objects.nonNull(param.getStarLevel())) {
            queryBuilder.must(QueryBuilders.termQuery("starLevel", param.getStarLevel()));
        }
        //舒适度精准查询
        if (Objects.nonNull(param.getComfortLevel())) {
            queryBuilder.must(QueryBuilders.termQuery("comfortLevel", param.getComfortLevel()));
        }
        //甄选酒店精准查询
        if (Objects.nonNull(param.getVipFlag())) {
            queryBuilder.must(QueryBuilders.termQuery("vipFlag", param.getVipFlag()));
        }
        //是否可用优惠券精准查询
        if (Objects.nonNull(param.getEnabledCoupon())) {
            queryBuilder.must(QueryBuilders.termQuery("enabledCoupon", param.getEnabledCoupon()));
        }
        //状态精准查询
        if (Objects.nonNull(param.getStatus())) {
            queryBuilder.must(QueryBuilders.termQuery("status", param.getStatus()));
        }
        //评分范围查询
        if (Objects.nonNull(param.getMinScore()) && Objects.isNull(param.getMaxScore())) {
            queryBuilder.must(QueryBuilders.rangeQuery("score").gte(param.getMinScore()));
        }
        if (Objects.isNull(param.getMinScore()) && Objects.nonNull(param.getMaxScore())) {
            queryBuilder.must(QueryBuilders.rangeQuery("score").lte(param.getMaxScore()));
        }
        if (Objects.nonNull(param.getMinScore()) && Objects.nonNull(param.getMaxScore())) {
            queryBuilder.must(QueryBuilders.rangeQuery("score").gte(param.getMinScore()).lte(param.getMaxScore()));
        }
        //是否启用精准查询
        if (Objects.nonNull(param.getValid())) {
            queryBuilder.must(QueryBuilders.termQuery("valid", param.getValid()));
        }
        //是否删除精准查询
        if (Objects.nonNull(param.getIsDel())) {
            queryBuilder.must(QueryBuilders.termQuery("isDel", param.getIsDel()));
        }
        //手动抢单标志精准查询
        if (Objects.nonNull(param.getManualPickOrder())) {
            queryBuilder.must(QueryBuilders.termQuery("manualPickOrder", param.getManualPickOrder()));
        }
        //酒店联系人精准查询
        if (StrUtil.isNotBlank(param.getContactName())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("contactName", "*" + param.getContactName() + "*"));
        }
        //酒店联系人电话精准查询
        if (StrUtil.isNotBlank(param.getPhone())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("phone", "*" + param.getPhone() + "*"));
        }
        //酒店固话精准查询
        if (StrUtil.isNotBlank(param.getTel())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("tel", "*" + param.getTel() + "*"));
        }
        BasalSearchParam basalSearchParam = new BasalSearchParam();
        basalSearchParam.setPageNo(param.getPageNo());
        basalSearchParam.setPageSize(param.getPageSize());
        if (esSorts.size() > 0) {
            basalSearchParam.setEsSorts(esSorts);
        }
        return search(queryBuilder, basalSearchParam);
    }

    /**
     * 酒店搜索(封装)
     *
     * @param queryBuilder
     * @param param
     * @return
     */
    public List<EsHotelInfoVo> search(QueryBuilder queryBuilder, BasalSearchParam param) {
        String indexs = EsIndexConstant.INDEX_HOTEL2;
        List<EsHotelInfoVo> esHotelInfoVos = Lists.newArrayList();
        long time = System.currentTimeMillis();
        SearchResponse searchResponse = elasticSearchService.search(queryBuilder, param, indexs);
        log.info("底层es搜索耗时time = {}", System.currentTimeMillis() - time);
        if (null == searchResponse) {
            return esHotelInfoVos;
        }
        SearchHits hits = searchResponse.getHits();
//        log.info("查询到的总结果是：total = {}", hits.getTotalHits());
        total = hits.getTotalHits();
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String sourceAsString = hit.getSourceAsString();
            if (StrUtil.isNotBlank(sourceAsString)) {
                EsHotelInfoVo esHotelInfoVo = JSONUtil.toBean(sourceAsString, EsHotelInfoVo.class);
                if (null != esHotelInfoVo && null != hit.getInnerHits()) {
                    //添加距离信息
                    if (null != param.getPositionX() && null != param.getPositionY()) {
                        esHotelInfoVo.setDistance(Double.valueOf(GeoDistance.ARC.calculate(
                                param.getPositionY(),
                                param.getPositionX(),
                                esHotelInfoVo.getLocation().getLat(),
                                esHotelInfoVo.getLocation().getLon(), DistanceUnit.METERS)).intValue());
                    }
                    List<EsRoomInfoVo> esRoomInfoVos = Lists.newArrayList();
                    for (Map.Entry<String, SearchHits> roomEntry : hit.getInnerHits().entrySet()) {
                        for (SearchHit roomHit : roomEntry.getValue().getHits()) {
                            EsRoomInfoVo esRoomInfoVo = JSONUtil.toBean(roomHit.getSourceAsString(), EsRoomInfoVo.class);
                            if (null != esRoomInfoVo && null != roomHit.getInnerHits()) {
                                List<EsRoomCalendarInfo> esRoomCalendarInfos = Lists.newArrayList();
                                for (Map.Entry<String, SearchHits> calendarEntry : roomHit.getInnerHits().entrySet()) {
                                    for (SearchHit calendarHit : calendarEntry.getValue().getHits()) {
                                        EsRoomCalendarInfo esRoomCalendarInfo = JSONUtil.toBean(calendarHit.getSourceAsString(), EsRoomCalendarInfo.class);
                                        esRoomCalendarInfos.add(esRoomCalendarInfo);
                                    }
                                }
                                esRoomInfoVo.setEsRoomCalendarInfos(esRoomCalendarInfos);
                            }
                            esRoomInfoVos.add(esRoomInfoVo);
                        }
                    }
                    esHotelInfoVo.setEsRoomInfoVos(esRoomInfoVos);
                }
                esHotelInfoVos.add(esHotelInfoVo);
            }
        }
        return esHotelInfoVos;
    }

    /**
     * 酒店搜索(封装)
     *
     * @param queryBuilder
     * @param param
     * @return
     */
    public Long searchCount(QueryBuilder queryBuilder, BasalSearchParam param) {
        long time = System.currentTimeMillis();
        SearchResponse searchResponse = elasticSearchService.search(queryBuilder, param, EsIndexConstant.INDEX_HOTEL2);
        log.info("底层es搜索耗时time = {}", System.currentTimeMillis() - time);
        if (null == searchResponse) {
            return 0L;
        }
        SearchHits hits = searchResponse.getHits();
        return hits.getTotalHits();
    }

    /**
     * 酒店搜索(封装相同酒店去重)
     *
     * @param queryBuilder
     * @param param
     * @return
     */
    public List<EsHotelInfoVo> searchRepeatHotel(QueryBuilder queryBuilder, BasalSearchParam param) {
        List<EsHotelInfoVo> result = search(queryBuilder, param);
        if (0 == result.size()) {
            return result;
        }
        Set<EsHotelInfoVo> set = new TreeSet<>(Comparator.comparing(EsHotelInfoVo::getSourceId));
        set.addAll(result);
        //去重后集合
        return Lists.newArrayList(set);
    }

    /**
     * c2b酒店搜索(按照距离)
     *
     * @param param
     * @return
     */
    @Override
    public List<EsHotelInfoVo> c2bSearchHotel(EsC2bHotelSearchParam param) {
        checkParam(param);
        if (param.getCity().contains("市")) {
            param.setCity(param.getCity().replaceAll("市", ""));
        }
        //三级房态查询条件
        BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
        //房态时间
        boolQueryBuilder3.must(QueryBuilders.rangeQuery("date").gte(param.getStartDate()).lt(param.getEndDate()));
        //房态销售价格(手动抢单的话大于售价，自动抢单的话区间)
        boolQueryBuilder3.must(QueryBuilders.rangeQuery("salePrice").gte(param.getPrice()));
        if (0 == param.getManualPickOrder()) {
            boolQueryBuilder3.must(QueryBuilders.rangeQuery("bottomPrice").lte(param.getPrice()));
        }
        //房态状态为可用
//        boolQueryBuilder3.must(QueryBuilders.termQuery("status", 1));
        boolQueryBuilder3.must(QueryBuilders.rangeQuery("status").gte(1).lte(1));
        //房间剩余数
        boolQueryBuilder3.must(QueryBuilders.rangeQuery("count").gt(0));
        HasChildQueryBuilder hasChildQueryBuilder3 = JoinQueryBuilders.hasChildQuery(EsIndexConstant.JOIN_TYPE_CALENDAR, boolQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder3.innerHit(new InnerHitBuilder().setSize(100));
        //二级房间
        HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, hasChildQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder2.innerHit(new InnerHitBuilder().setSize(100));
        //创建距离查询对象
        GeoDistanceQueryBuilder geoDistanceQueryBuilder = new GeoDistanceQueryBuilder("location")
                .distance(param.getDistance(), DistanceUnit.METERS).point(param.getPositionY(), param.getPositionX());
        //创建复合查询对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(hasChildQueryBuilder2);
        queryBuilder.must(geoDistanceQueryBuilder);
        //酒店查询条件
        //状态为已通过(3)精准查询
        queryBuilder.must(QueryBuilders.termQuery("status", 3));
        //是否启用精准查询
        queryBuilder.must(QueryBuilders.termQuery("valid", true));
        //是否删除精准查询
        queryBuilder.must(QueryBuilders.termQuery("isDel", 1));
        //舒适度精准查询
        BoolQueryBuilder comfortLevelBuilder = QueryBuilders.boolQuery();
        if (StrUtil.isNotBlank(param.getComfortLevel())) {
            List<String> comfortLevels = Splitters.splitToString(param.getComfortLevel(), Splitters.COMMA);
            for (String comfortLevel : comfortLevels) {
                comfortLevelBuilder.should(QueryBuilders.termQuery("comfortLevel", comfortLevel));
            }
        }
        queryBuilder.must(comfortLevelBuilder);
        //手动抢单标志精准查询
        if (Objects.nonNull(param.getManualPickOrder())) {
            queryBuilder.must(QueryBuilders.termQuery("manualPickOrder", param.getManualPickOrder()));
        }
        //是否可用优惠券精准查询
        if (Objects.nonNull(param.getEnabledCoupon())) {
            queryBuilder.must(QueryBuilders.termQuery("enabledCoupon", param.getEnabledCoupon()));
        }
        //sourceType精准查询
        if (Objects.nonNull(param.getSourceType())) {
            queryBuilder.must(QueryBuilders.termQuery("sourceType", param.getSourceType()));
        }
        //地址模糊查询
        if (StrUtil.isNotBlank(param.getCity())) {
            queryBuilder.must(QueryBuilders.wildcardQuery("address", "*" + param.getCity() + "*"));
        }
        BasalSearchParam basalSearchParam = new BasalSearchParam();
        basalSearchParam.setPageNo(param.getPageNo());
        basalSearchParam.setPageSize(param.getPageSize());
        basalSearchParam.setPositionX(param.getPositionX());
        basalSearchParam.setPositionY(param.getPositionY());
        long esTime = System.currentTimeMillis();
        List<EsHotelInfoVo> result = search(queryBuilder, basalSearchParam);
        log.info("{}公里es返回结果耗时：time = {}毫秒,搜到记录{}条", param.getDistance() / 1000, (System.currentTimeMillis() - esTime),result.size());
        long time = System.currentTimeMillis();
        Set<EsHotelInfoVo> set = new TreeSet<>(Comparator.comparing(EsHotelInfoVo::getSourceId));
        set.addAll(result);
        //去重后集合
        List<EsHotelInfoVo> collect = Lists.newArrayList(set);
        log.info("{}公里去重耗时：time = {}毫秒，结果{}条", param.getDistance() / 1000, (System.currentTimeMillis() - time),result.size());
        List<EsHotelInfoVo> result2 = Lists.newArrayList();
        long between = DateUtil.between(param.getStartDate(), param.getEndDate(), DateUnit.DAY);
        for (EsHotelInfoVo esHotelInfoVo : collect) {
            for (EsRoomInfoVo esRoomInfoVo : esHotelInfoVo.getEsRoomInfoVos()) {
                if (between == esRoomInfoVo.getEsRoomCalendarInfos().size()) {
                    result2.add(esHotelInfoVo);
                    break;
                } else {
                    log.info("移除的酒店id为hotelSourceId = {}", esHotelInfoVo.getSourceId());
                }
            }
        }
        return result2;
    }

    /**
     * c2b酒店搜索(按照距离渐进)
     *
     * @param c2bHotelSearchParam
     * @return
     */
    @Override
    public List<EsHotelInfoVo> c2bSearchStepHotel(EsC2bHotelSearchParam c2bHotelSearchParam) {
        long startTime = System.currentTimeMillis();
        //获取c2b搜索条件
        Map condition = getC2bSearchCondition();
        //c2b搜索距离
        int distance = null == condition.get("distance") ? 20000 : (Integer) condition.get("distance");
//        if (distance <= 5000) {
//            distance = 20000;
//        }
        //c2b搜索个数
        int minCount = null == condition.get("minCount") ? 15 : (Integer) condition.get("minCount");
        int maxAmount = null == condition.get("maxCount") ? 25 : (Integer) condition.get("maxCount");
        checkSearchDate(c2bHotelSearchParam.getStartDate(), c2bHotelSearchParam.getEndDate());
        c2bHotelSearchParam.setDistance(distance);
        //执行搜索
        List<EsHotelInfoVo> esHotelInfoVos = c2bSearchHotel(c2bHotelSearchParam);
        //自营的
        List<EsHotelInfoVo> yyksEsHotelInfoVos = esHotelInfoVos.stream().filter(e -> HotelSourceType.YYKS.equals(e.getSourceType())).collect(Collectors.toList());
        //cn的
        List<EsHotelInfoVo> cnEsHotelInfoVos = esHotelInfoVos.stream().filter(e -> HotelSourceType.CN.equals(e.getSourceType())).collect(Collectors.toList());
        //拼装最终结果
        List<EsHotelInfoVo> searchResults = Lists.newArrayList();
        //获取一个随机数
        //int randomCount = RandomUtil.randomInt(minCount, maxAmount + 1);
        int randomCount = RandomUtil.randomInt(10, 15);
        if (yyksEsHotelInfoVos.size() == 0) {
            if (cnEsHotelInfoVos.size() == 0) {
                return searchResults;
            } else {
                searchResults = cnEsHotelInfoVos;
            }
        } else if (yyksEsHotelInfoVos.size() < randomCount) {
            int yyksCount = yyksEsHotelInfoVos.size();
            int remain = randomCount - yyksCount;
            if (cnEsHotelInfoVos.size() == 0) {
                searchResults = yyksEsHotelInfoVos;
            } else if (cnEsHotelInfoVos.size() < remain) {
                yyksEsHotelInfoVos.addAll(cnEsHotelInfoVos);
                searchResults = yyksEsHotelInfoVos;
            } else {
                cnEsHotelInfoVos.sort(Comparator.comparing(EsHotelInfoVo::getDistance));
                yyksEsHotelInfoVos.addAll(cnEsHotelInfoVos.subList(0, remain));
                searchResults = yyksEsHotelInfoVos;
            }
        } else {
            searchResults = yyksEsHotelInfoVos;
        }
        //将最终结果按照距离排序
        searchResults.sort(Comparator.comparing(EsHotelInfoVo::getDistance));
        if (searchResults.size() >= randomCount) {
            searchResults = searchResults.subList(0, randomCount);
        }
        log.info("c2b搜索随机结果为randomCount = {}, 总耗时：time = {}秒", randomCount, (System.currentTimeMillis() - startTime) / 1000);
        return searchResults;
    }

    /**
     * 根据酒店地址查询酒店信息
     *
     * @param param
     * @return
     */
    @Override
    public List<EsHotelInfoVo> searchHotelByAddress(EsHotelCitySearchParam param) {
        if (param.getAddress().contains("市")) {
            param.setAddress(param.getAddress().replaceAll("市", ""));
        }
        //三级房态查询条件
        BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
        //房态时间
        boolQueryBuilder3.must(QueryBuilders.termQuery("date", param.getDateForPrice()));
        HasChildQueryBuilder hasChildQueryBuilder3 = JoinQueryBuilders.hasChildQuery(EsIndexConstant.JOIN_TYPE_CALENDAR, boolQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder3.innerHit(new InnerHitBuilder().setSize(1));
        //二级房间
        HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, hasChildQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder2.innerHit(new InnerHitBuilder().setSize(50));
        //创建复合查询对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(hasChildQueryBuilder2);
        //酒店查询条件
        //地址模糊查询
        if (StrUtil.isNotBlank(param.getAddress())) {
            ImportCityDataConfig importCityDataConfig = importCityDataConfigService.getOne(new QueryWrapper<ImportCityDataConfig>().like(ImportCityDataConfig.CITY_NAME, param.getAddress()));
            //能匹配到城市
            if (null != importCityDataConfig) {
                queryBuilder.must(QueryBuilders.termQuery("cityId", importCityDataConfig.getCityId()));
            } else {
                queryBuilder.must(QueryBuilders.wildcardQuery("address", "*" + param.getAddress() + "*"));
            }
        }
        //sourceType精准查询
        if (Objects.nonNull(param.getSourceType())) {
            queryBuilder.must(QueryBuilders.termQuery("sourceType", param.getSourceType()));
        }
        BasalSearchParam basalSearchParam = new BasalSearchParam();
        basalSearchParam.setPageNo(param.getPageNo());
        basalSearchParam.setPageSize(param.getPageSize());
        return searchRepeatHotel(queryBuilder, basalSearchParam);
    }

    /**
     * 查询某个城市的所有酒店房态
     *
     * @param param
     * @return
     */
    @Override
    public List<EsHotelInfoVo> searchHotelByCityName(EsHotelRoomCitySearchParam param) {
        //二级房间
        HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, new BoolQueryBuilder(), ScoreMode.None);
        hasChildQueryBuilder2.innerHit(new InnerHitBuilder().setSize(50));
        //创建复合查询对象
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(hasChildQueryBuilder2);
        //酒店查询条件
        if (StrUtil.isNotBlank(param.getCityName())) {
            ImportCityDataConfig importCityDataConfig = importCityDataConfigService.getOne(new QueryWrapper<ImportCityDataConfig>().like(ImportCityDataConfig.CITY_NAME, param.getCityName()));
            //能匹配到城市
            if (null != importCityDataConfig) {
                queryBuilder.must(QueryBuilders.termQuery("cityId", importCityDataConfig.getCityId()));
            } else {
                queryBuilder.must(QueryBuilders.wildcardQuery("address", "*" + param.getCityName() + "*"));
            }
        }
        //sourceType精准查询
        if (Objects.nonNull(param.getSourceType())) {
            queryBuilder.must(QueryBuilders.termQuery("sourceType", param.getSourceType()));
        }
        BasalSearchParam basalSearchParam = new BasalSearchParam();
        basalSearchParam.setPageNo(param.getPageNo());
        basalSearchParam.setPageSize(param.getPageSize());
        return searchRepeatHotel(queryBuilder, basalSearchParam);
    }

    /**
     * @param param
     * @return
     */
    @Override
    public List<ExportCnHotelResult> exportCnHotel(CnHotelListParam param) {
        Arguments.strIsNull(param.getCityName(), "城市名参数为空");
        List<ExportCnHotelResult> exportCnHotelList = Lists.newArrayList();
        try {
            //创建复合查询对象
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
            //酒店来源精准查询
            queryBuilder.must(QueryBuilders.termQuery("sourceType", param.getSourceType()));
            //酒店查询条件
            QueryWrapper<ImportCityDataConfig> queryWrapper = new QueryWrapper<>();
            queryWrapper.like(ImportCityDataConfig.CITY_NAME, param.getCityName());
            ImportCityDataConfig importCityDataConfig = importCityDataConfigService.getOne(queryWrapper);
            //能匹配到城市
            if (importCityDataConfig != null) {
                queryBuilder.must(QueryBuilders.termQuery("cityId", importCityDataConfig.getCityId()));
            } else {
                queryBuilder.must(QueryBuilders.wildcardQuery("address", "*" + param.getCityName() + "*"));
            }
            BasalSearchParam basalSearchParam = new BasalSearchParam();
            basalSearchParam.setPageNo(1);
            basalSearchParam.setPageSize(10000);
            List<EsHotelInfoVo> esHotelInfoVos = searchRepeatHotel(queryBuilder, basalSearchParam);
            for (EsHotelInfoVo esHotelInfoVo : esHotelInfoVos) {
                ExportCnHotelResult exportCnHotelResult = new ExportCnHotelResult();
                exportCnHotelResult.setAddress(esHotelInfoVo.getAddress());
                exportCnHotelResult.setHotelName(esHotelInfoVo.getName());
                exportCnHotelResult.setHotelPhone(esHotelInfoVo.getPhone());
                exportCnHotelResult.setSourceType(esHotelInfoVo.getSourceType());
                // exportCnHotelResult.setCityName(param.getCityName());
                exportCnHotelList.add(exportCnHotelResult);
            }
            return exportCnHotelList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return exportCnHotelList;
    }

    /**
     * 查询CNBooking酒店详情
     *
     * @param sourceId
     * @return
     */
    @Override
    public EsHotelInfoDetailVo searchCnHotelDetail(String sourceId) {
        return searchSingleCnHotelDetail(sourceId, null);
    }

    /**
     * 通过hotelId查询CNBooking酒店详情
     *
     * @param hotelId
     * @return
     */
    @Override
    public EsHotelInfoDetailVo searchCnHotelByHotelId(String hotelId) {
        return searchSingleCnHotelDetail(null, hotelId);
    }

    /**
     * 数据大屏之舒适度个数查询
     *
     * @param param {@link HotelCountParam}
     * @return
     */
    @Override
    public Integer searchHotelCount(HotelCountParam param) {
        try {
            //创建复合查询对象
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
            //是否启用精准查询
            queryBuilder.must(QueryBuilders.termQuery("valid", true));
            //是否删除精准查询
            queryBuilder.must(QueryBuilders.termQuery("isDel", 1));
            //舒适度精准查询
            if (null != param.getComfortLevel()) {
                queryBuilder.must(QueryBuilders.termQuery("comfortLevel", param.getComfortLevel()));
            }
            //舒适度精准查询
            if (null != param.getProvinceId()) {
                queryBuilder.must(QueryBuilders.termQuery("provinceId", param.getProvinceId()));
            }
            return searchCount(queryBuilder, new BasalSearchParam()).intValue();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 查询唯一酒店详情信息
     *
     * @param sourceId
     * @return
     */
    private EsHotelInfoDetailVo searchSingleCnHotelDetail(String sourceId, String hotelId) {
        EsHotelInfoDetailVo esHotelInfoDetailVo = new EsHotelInfoDetailVo();
        try {
            //创建复合查询对象
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
            queryBuilder.must(getHasChildQueryBuilder());
            queryBuilder.must(QueryBuilders.termQuery("sourceType", "cn"));
            if (!StrUtil.isBlank(sourceId)) {
                queryBuilder.must(QueryBuilders.termQuery("sourceId", sourceId));
            }
            if (!StrUtil.isBlank(hotelId)) {
                queryBuilder.must(QueryBuilders.termQuery("hotelId", hotelId));
            }
            BasalSearchParam basalSearchParam = new BasalSearchParam();
            basalSearchParam.setPageNo(1);
            basalSearchParam.setPageSize(1);
            List<EsHotelInfoVo> esHotelInfoVoLists = searchRepeatHotel(queryBuilder, basalSearchParam);
            for (EsHotelInfoVo esHotelInfoVoList : esHotelInfoVoLists) {
                esHotelInfoDetailVo = ObjectConvertUtil.convertObject(esHotelInfoVoList, EsHotelInfoDetailVo.class);
                List<RoomInfo> roomInfos = ObjectConvertUtil.convertList(esHotelInfoVoList.getEsRoomInfoVos(), RoomInfo.class);
                if (null != roomInfos && roomInfos.size() > 0) {
                    esHotelInfoDetailVo.setRoomInfos(roomInfos);
                }
            }
            return esHotelInfoDetailVo;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return esHotelInfoDetailVo;
    }

    private HasChildQueryBuilder getHasChildQueryBuilder() {
        //三级房态查询条件
        //房态时间
        HasChildQueryBuilder hasChildQueryBuilder3 = JoinQueryBuilders.hasChildQuery(EsIndexConstant.JOIN_TYPE_CALENDAR, new BoolQueryBuilder(), ScoreMode.None);
        hasChildQueryBuilder3.innerHit(new InnerHitBuilder().setSize(50));
        //二级房间
        HasChildQueryBuilder hasChildQueryBuilder2 = new HasChildQueryBuilder(EsIndexConstant.JOIN_TYPE_ROOM, hasChildQueryBuilder3, ScoreMode.None);
        hasChildQueryBuilder2.innerHit(new InnerHitBuilder().setSize(50));
        return hasChildQueryBuilder2;
    }

    /**
     * 查询CNBooking酒店
     *
     * @param param
     * @return
     */
    @Override
    public CnHotelListResultCount searchCnHotel(CnHotelListParam param) {
        CnHotelListResultCount cnHotelListResultCount = new CnHotelListResultCount();
        try {
            List<CnHotelListResult> cnHotelListResults = Lists.newArrayList();
            //创建复合查询对象
            BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
            queryBuilder.must(getHasChildQueryBuilder());
            //酒店来源精准查询
            queryBuilder.must(QueryBuilders.termQuery("sourceType", param.getSourceType()));
            if (Arguments.strNotNull(param.getCityCode())) {
                queryBuilder.must(QueryBuilders.termQuery("cityId", param.getCityCode()));
            } else if (Arguments.strNotNull(param.getProvinceCode())) {
                queryBuilder.must(QueryBuilders.termQuery("provinceId", param.getProvinceCode()));
            }
            //酒店查询条件
            if (StrUtil.isNotBlank(param.getKeyword())) {
                QueryWrapper<ImportCityDataConfig> queryWrapper = new QueryWrapper<>();
                queryWrapper.like(ImportCityDataConfig.CITY_NAME, param.getKeyword());
                ImportCityDataConfig importCityDataConfig = importCityDataConfigService.getOne(queryWrapper);
                if (importCityDataConfig != null) //能匹配到城市
                {
                    queryBuilder.must(QueryBuilders.termQuery("cityId", importCityDataConfig.getCityId()));
                } else {
                    queryBuilder.must(QueryBuilders.wildcardQuery("name", "*" + param.getKeyword() + "*"));
                }
            }
            //sourceType精准查询
            if (Objects.nonNull(param.getStarLevel())) {
                queryBuilder.must(QueryBuilders.termQuery("starLevel", param.getStarLevel()));
            }
            BasalSearchParam basalSearchParam = new BasalSearchParam();
            basalSearchParam.setPageNo(param.getPageNum());
            basalSearchParam.setPageSize(param.getPageSize());
            List<EsHotelInfoVo> esHotelInfoVos = searchRepeatHotel(queryBuilder, basalSearchParam);
            for (EsHotelInfoVo esHotelInfoVo : esHotelInfoVos) {
                CnHotelListResult hotelListResult = new CnHotelListResult();
                hotelListResult.setAddress(esHotelInfoVo.getAddress());
                hotelListResult.setHotelName(esHotelInfoVo.getName());
                hotelListResult.setHotelPhone(esHotelInfoVo.getPhone());
                hotelListResult.setSourceType(esHotelInfoVo.getSourceType());
                hotelListResult.setHotelSourceId(esHotelInfoVo.getSourceId());
                hotelListResult.setDocId(esHotelInfoVo.getHotelId());
                hotelListResult.setCityId(esHotelInfoVo.getCityId());
                cnHotelListResults.add(hotelListResult);
            }
            cnHotelListResultCount.setCnHotelListResults(cnHotelListResults);
            cnHotelListResultCount.setTotal(total);
            return cnHotelListResultCount;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cnHotelListResultCount;
    }

    /**
     * c2b搜索条件
     *
     * @return
     */
    private Map getC2bSearchCondition() {
        try {
            Config config = configMapper.selectValueByKey(ConfigKey.C2B_SEARCH_CONDITION);
            String value = config.getValue();
            return JSONUtil.toBean(value, Map.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Maps.newHashMap();
    }

    /**
     * 检查c2b酒店搜索参数
     *
     * @param param
     */
    private void checkParam(EsC2bHotelSearchParam param) {
        Arguments.strIsNull(param.getCity(), "城市必填");
        Arguments.intIsNull(param.getPrice(), "价格必填");
        Arguments.beanIsNull(param.getPositionX(), "经度必填");
        Arguments.beanIsNull(param.getPositionY(), "纬度必填");
        Arguments.beanIsNull(param.getStartDate(), "开始时间必填");
        Arguments.beanIsNull(param.getEndDate(), "结束时间必填");
        Arguments.intIsNull(param.getDistance(), "距离必填");
        Arguments.intIsNull(param.getManualPickOrder(), "抢单状态必填");
    }

    private void checkSearchDate(Date startDate, Date endDate) {
        //时间校验
        DateTime todayBegin = DateUtil.beginOfDay(new Date());
        DateTime tomorrowBegin = DateUtil.offsetDay(todayBegin, 1);
        DateTime ThirtyDaysLaterBegin = DateUtil.offsetDay(todayBegin, 30);
        if (startDate.after(endDate)
                || startDate.before(todayBegin)
                || endDate.before(tomorrowBegin)
                || endDate.after(ThirtyDaysLaterBegin)) {
            throw new ServiceException("请求参数不正确");
        }
    }
}
